import * as vscode from 'vscode';
import { existsSync, readFileSync, readdirSync, writeFileSync } from 'fs';

type Node = {
    lang: string,
    snippets?: {
        snippet: string,
        code?: string,
    }[]
}

interface ViewMessage {
    command: 'addLang' | 'addSnippet' | 'addCode' | 'update';
    lang: string;
    snippet?: string;
    code?: string;
    originLang?: string;
    originSnippet?: string;
    originCode?: string;
}

export class ViewProvider implements vscode.WebviewViewProvider {
    private readonly _context: vscode.ExtensionContext;
    private _view: vscode.WebviewView | undefined;

    constructor(context: vscode.ExtensionContext) {
        this._context = context;
    }

    private getUri(uri: vscode.Uri, ...pathSegements: string[]): vscode.Uri {
        return vscode.Uri.joinPath(uri, ...pathSegements);
    }

    resolveWebviewView(
        webviewView: vscode.WebviewView,
        context: vscode.WebviewViewResolveContext,
        token: vscode.CancellationToken): Thenable<void> | void {
        this._view = webviewView;
        webviewView.webview.options = { enableScripts: true };
        const baseUri = this.getUri(this._context.extensionUri, 'assets');
        const themesUri = this.getUri(baseUri, 'themes');
        const css = vscode.window.activeColorTheme.kind === vscode.ColorThemeKind.Dark
            ? 'github-dark.min.css'
            : 'vs.min.css';
        const cssUri = this.getUri(themesUri, css);
        const jsUri = this.getUri(themesUri, 'highlight.min.js');
        const codiconUri = this.getUri(baseUri, 'view', 'codicon.css');
        const htmlUri = this.getUri(baseUri, 'view', 'cheatsheet.html');
        const cheatsheetUri = this.getUri(baseUri, 'cheatsheets', 'cheatsheets.json');
        webviewView.webview.html = readFileSync(htmlUri.fsPath, 'utf-8')
            .replace('${codiconUri}', this.getViewUri(codiconUri))
            .replace('CSS-PLACEHOLDER', this.getViewUri(cssUri))
            .replace('CHEATSHEETS', this.getCheatsheets(cheatsheetUri.fsPath))
            .replace('JS-PLACEHOLDER', this.getViewUri(jsUri));
        webviewView.webview.onDidReceiveMessage((e: ViewMessage) => {
            this.saveCheatsheet(e, cheatsheetUri);
        });
    }

    private getCheatsheets(path: string): string {
        return JSON.stringify(this.loadCheatsheets(path));
    }

    private loadCheatsheets(path: string): Node[] {
        if (!existsSync(path)) {
            writeFileSync(path, '{}', 'utf-8');
        }

        const content = readFileSync(path, 'utf-8').trim();

        return content ? JSON.parse(content) : [];
    }

    private saveCheatsheet(e: ViewMessage, uri: vscode.Uri) {
        const path = uri.fsPath;
        let db: Node[] = this.loadCheatsheets(path);
        const command = e.command;
        switch (command) {
            case 'addLang':
                db.push({ lang: e.lang });
                break;
            case 'addSnippet':
                const snode = db.find(node => node.lang === e.lang)!;
                (snode.snippets ??= []).push({ snippet: e.snippet! });
                break;
            case 'addCode':
                const cnode = db.find(node => node.lang === e.lang)!;
                const snippet = cnode.snippets!.find(s => s.snippet === e.snippet)!;
                snippet.code = e.code;
                break;
            case 'update':
                if (e.originCode) {
                    const ucNode = db.find(node => node.lang === e.lang)!;
                    const ucSnippet = ucNode.snippets!.find(s => s.snippet === e.snippet)!;
                    ucSnippet.code = e.code;
                } else if (e.originSnippet) {
                    const usNode = db.find(node => node.lang === e.lang)!;
                    const usSnippet = usNode.snippets!.find(s => s.snippet === e.originSnippet)!;
                    usSnippet.snippet = e.snippet!;
                } else if (e.originLang) {
                    const uNode = db.find(node => node.lang === e.originLang);
                    uNode!.lang = e.lang;
                }
                break;
        }

        writeFileSync(path, JSON.stringify(db, null, 2), 'utf-8');
    }

    private getViewUri(uri: vscode.Uri): string {
        return this._view?.webview.asWebviewUri(uri).toString() ?? '';
    }
}